﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Remoting.Messaging;
using ESoft.Core.Data.Entity;
using ESoft.Core.Model;
using ESoft.Core.Web.VO;

namespace ESoft.Core.Data.DAO.Impl
{
    public class BaseDAO<T, TKey> : IBaseDAO<T, TKey> where T : BaseModel<TKey>, new()
    {
        private readonly IDbContextOption _dbContextOption;

        protected ESoftDbContext Db
        {
            get
            {
                //获取BaseDbContext的完全限定名，其实这个名字没什么特别的意义，仅仅是一个名字而已，也可以取别的名字的  
                string threadName = typeof(ESoftDbContext).FullName;

                //获取key为threadName的这个线程缓存（CallContext就是线程缓存容器类）  
                object dbObj = CallContext.GetData(threadName);

                //如果key为threadName的线程缓存不存在  
                if (dbObj == null)
                {
                    //创建BaseDbContext类的对象实例  
                    dbObj = new ESoftDbContext(_dbContextOption.GetConnetion());
                    //将这个BaseDbContext类的对象实例保存到线程缓存当中(以键值对的形式进行保存的，我这就将key设为当前线程的完全限定名了)  
                    CallContext.SetData(threadName, dbObj);
                    return dbObj as ESoftDbContext;
                }
                return dbObj as ESoftDbContext;
            }

        }

        //protected ESoftDbContext Db;

        public BaseDAO(IDbContextOption option)
        {
            _dbContextOption = option;
        }

        public virtual T Find(TKey id)
        {
            return Db.Set<T>().Find(id);
        }

        public virtual T First(Expression<Func<T, bool>> predicate)
        {
            return Db.Set<T>().FirstOrDefault(predicate);
        }

        public virtual T Add(T entity)
        {
            Db.Set<T>().Add(entity);
            Db.SaveChanges();
            return entity;
        }

        public virtual bool Update(T entity)
        {
            Db.Entry(entity).State = EntityState.Modified;
            Db.SaveChanges();
            return true;
        }

        public virtual bool Delete(TKey id)
        {
            return Delete(Find(id));
        }

        public virtual bool Delete(T entity)
        {
            Db.Entry(entity).State = EntityState.Deleted;
            Db.SaveChanges();
            return true;
        }

        public virtual bool Delete(IEnumerable<T> entities)
        {
            foreach (var entity in entities)
            {
                Delete(entity);
            }
            return true;
        }

        public virtual bool Exist(Expression<Func<T, bool>> predicate)
        {
            return Db.Set<T>().Any(predicate);
        }

        public virtual int Count(Expression<Func<T, bool>> predicate)
        {
            return Db.Set<T>().Count(predicate);
        }

        public virtual int Sum(Expression<Func<T, bool>> predicate, Expression<Func<T, int>> selector)
        {
            try
            {
                return Db.Set<T>().Where(predicate).Sum(selector);
            }
            catch
            {
                return 0;
            }
        }

        public virtual decimal Sum(Expression<Func<T, bool>> predicate, Expression<Func<T, decimal>> selector)
        {
            try
            {
                return Db.Set<T>().Where(predicate).Sum(selector);
            }
            catch
            {
                return 0;
            }
        }

        public virtual IQueryable<T> LoadEntities()
        {
            return Db.Set<T>();
        }

        public virtual IQueryable<T> LoadEntities(Expression<Func<T, bool>> predicate)
        {
            return Db.Set<T>().Where(predicate);
        }

        public virtual IQueryable<T> LoadPageEntities<TS>(PageInput page, Expression<Func<T, bool>> predicate, Expression<Func<T, TS>> keySelector, bool isAsc)
        {
            var lst = Db.Set<T>().Where(predicate);
            lst = isAsc ? lst.OrderBy(keySelector) : lst.OrderByDescending(keySelector);

            return lst.Skip(page.PageSize * (page.PageIndex - 1))
                  .Take(page.PageSize);
        }


    }
}
